//https://www.lmlphp.com/user/1582/article/item/356684/
class ModBusTcpMasterUtils {

    //事务处理标识
    static affairNum=0;
    //协议标识
    static protocolArr=[0,0];


    static getAffairArr(){
        ModBusTcpMasterUtils.affairNum++;
        if(ModBusTcpMasterUtils.affairNum>65535){
            ModBusTcpMasterUtils.affairNum=0;
        }
        return [ModBusTcpMasterUtils.affairNum >> 8 & 0xff,ModBusTcpMasterUtils.affairNum & 0xff]
    }
    /// <summary>
    /// 读取从1到2000的线圈状态
    /// </summary>
    /// <param name="slaveAddress">从设备的地址</param>
    /// <param name="startAddress">要读取起始地址</param>
    /// <param name="numberOfPoints">要读取的数量</param>
    /// <returns>线圈数据</returns>
    static readCoils(slaveAddress, startAddress, numberOfPoints) {
        const byteList = [];
        let aAffairArr =ModBusTcpMasterUtils.getAffairArr();
        //事务处理标识
        byteList.push(aAffairArr[0]);
        byteList.push(aAffairArr[1]);
        //协议标识符
        byteList.push(0);
        byteList.push(0);
        //长度
        byteList.push(0);
        byteList.push(6);
        //单元标识符
        byteList.push(slaveAddress);

        //功能码
        byteList.push(0x01);
        //起始地址
        byteList.push(startAddress >> 8 & 0xff);
        byteList.push(startAddress & 0xff);
        //数量
        byteList.push(numberOfPoints >> 8 & 0xff);
        byteList.push(numberOfPoints & 0xff);
        return byteList;
    }


    /// <summary>
    ///    读取1到2000离散输入的状态
    /// </summary>
    /// <param name="slaveAddress">从设备的地址</param>
    /// <param name="startAddress">要读取起始地址</param>
    /// <param name="numberOfPoints">要读取的数量</param>
    /// <returns>离散数据</returns>
    static readInputs(slaveAddress, startAddress, numberOfPoints) {
        const byteList = [];
        let aAffairArr =ModBusTcpMasterUtils.getAffairArr();
        //事务处理标识
        byteList.push(aAffairArr[0]);
        byteList.push(aAffairArr[1]);
        //协议标识符
        byteList.push(0);
        byteList.push(0);
        //长度
        byteList.push(0);
        byteList.push(6);
        //单元标识符
        byteList.push(slaveAddress);

        //功能码
        byteList.push(0x02);
        //起始地址
        byteList.push((startAddress >> 8 & 0xff));
        byteList.push((startAddress & 0xff));
        //数量
        byteList.push((numberOfPoints >> 8 & 0xff));
        byteList.push((numberOfPoints & 0xff));
        return byteList;
    }

    /// <summary>
    ///    读取保持寄存器数据。
    /// </summary>
    /// <param name="slaveAddress">从设备的地址</param>
    /// <param name="startAddress">要读取起始地址</param>
    /// <param name="numberOfPoints">要读取的数量</param>
    /// <returns>保持寄存器数据</returns>
    static readHoldingRegisters(slaveAddress, startAddress, numberOfPoints) {
        const byteList = [];
        let aAffairArr =ModBusTcpMasterUtils.getAffairArr();
        //事务处理标识
        byteList.push(aAffairArr[0]);
        byteList.push(aAffairArr[1]);
        //协议标识符
        byteList.push(0);
        byteList.push(0);
        //长度
        byteList.push(0);
        byteList.push(6);
        //单元标识符
        byteList.push(slaveAddress);
        //功能码
        byteList.push(0x03);
        //起始地址
        byteList.push((startAddress >> 8 & 0xff));
        byteList.push((startAddress & 0xff));
        //数量
        byteList.push((numberOfPoints >> 8 & 0xff));
        byteList.push((numberOfPoints & 0xff));

        //crc
        let crc = CrcUtils.CRC16(byteList);
        byteList.push(crc[0]);
        byteList.push(crc[1]);
        return byteList;
    }

    /// <summary>
    /// 读取输入寄存器数据。
    /// </summary>
    /// <param name="slaveAddress">从设备的地址</param>
    /// <param name="startAddress">要读取起始地址</param>
    /// <param name="numberOfPoints">要读取的数量</param>
    /// <returns>输入寄存器数据</returns>
    static readInputRegisters(slaveAddress, startAddress, numberOfPoints) {

        const byteList = [];
        let aAffairArr =ModBusTcpMasterUtils.getAffairArr();
        //事务处理标识
        byteList.push(aAffairArr[0]);
        byteList.push(aAffairArr[1]);
        //协议标识符
        byteList.push(0);
        byteList.push(0);
        //长度
        byteList.push(0);
        byteList.push(6);
        //单元标识符
        byteList.push(slaveAddress);
        //功能码
        byteList.push(0x04);
        //起始地址
        byteList.push((startAddress >> 8 & 0xff));
        byteList.push((startAddress & 0xff));
        //数量
        byteList.push((numberOfPoints >> 8 & 0xff));
        byteList.push((numberOfPoints & 0xff));

        //crc
        let crc = CrcUtils.CRC16(byteList);
        byteList.push(crc[0]);
        byteList.push(crc[1]);
        return byteList;
    }

    /// <summary>
    ///  写单个线圈。
    /// </summary>
    /// <param name="slaveAddress">从设备的地址</param>
    /// <param name="startAddress">要写入起始地址</param>
    /// <param name="value">要写入的值</param>
    static writeSingleCoil(slaveAddress, startAddress, value) {
        const byteList = [];
        let aAffairArr =ModBusTcpMasterUtils.getAffairArr();
        //事务处理标识
        byteList.push(aAffairArr[0]);
        byteList.push(aAffairArr[1]);
        //协议标识符
        byteList.push(0);
        byteList.push(0);
        //长度
        byteList.push(0);
        byteList.push(6);
        //单元标识符
        byteList.push(slaveAddress);
        //功能码
        byteList.push(0x05);
        //起始地址
        byteList.push((startAddress >> 8 & 0xff));
        byteList.push((startAddress & 0xff));
        //值
        if (value > 0) {
            byteList.push(0xff);
            byteList.push(0x00);
        } else {
            byteList.push(0x00);
            byteList.push(0x00);
        }

        //crc
        let crc = CrcUtils.CRC16(byteList);
        byteList.push(crc[0]);
        byteList.push(crc[1]);
        return byteList;
    }


    /// <summary>
    ///  写单个保持寄存器。
    /// </summary>
    /// <param name="slaveAddress">从设备的地址</param>
    /// <param name="startAddress">要写入的起始地址</param>
    /// <param name="value">要写入的值</param>
    static writeSingleRegister(slaveAddress, startAddress, value) {
        const byteList = [];
        let aAffairArr =ModBusTcpMasterUtils.getAffairArr();
        //事务处理标识
        byteList.push(aAffairArr[0]);
        byteList.push(aAffairArr[1]);
        //协议标识符
        byteList.push(0);
        byteList.push(0);
        //长度
        byteList.push(0);
        byteList.push(6);
        //单元标识符
        byteList.push(slaveAddress);
        //功能码
        byteList.push(0x06);
        //起始地址
        byteList.push((startAddress >> 8 & 0xff));
        byteList.push((startAddress & 0xff));
        //值
        byteList.push((value >> 8 & 0xff));
        byteList.push((value & 0xff));
        //crc
        let crc = CrcUtils.CRC16(byteList);
        byteList.push(crc[0]);
        byteList.push(crc[1]);
        return byteList;
    }

    /// <summary>
    ///  写多个保持寄存器数据（1到123，限制了数据，如要扩展需要修改）
    /// </summary>
    /// <param name="slaveAddress">从设备的地址</param>
    /// <param name="startAddress">要写入的起始地址</param>
    /// <param name="data">要写入的值</param>
    static writeMultipleRegisters(slaveAddress, startAddress, data) {
        //寄存器数
        let regCount = data.length;
        let aAffairArr =ModBusTcpMasterUtils.getAffairArr();
        //事务处理标识
        byteList.push(aAffairArr[0]);
        byteList.push(aAffairArr[1]);
        //协议标识符
        byteList.push(0);
        byteList.push(0);
        //长度
        byteList.push(0);
        byteList.push(regCount*2+7);
        //单元标识符
        byteList.push(slaveAddress);
        //功能码
        byteList.push(0x10);
        //起始地址
        byteList.push((startAddress >> 8 & 0xff));
        byteList.push((startAddress & 0xff));

        byteList.push((regCount >> 8 & 0xff));
        byteList.push((regCount & 0xff));

        //字节数
        let byteCount = (regCount * 2);
        byteList.push((byteCount & 0xff));
        //值
        for (let i = 0; i < data.length; i++) {
            //高位的先发送
            byteList.push((data[data.length - i - 1] >> 8 & 0xff));
            byteList.push((data[data.length - i - 1] & 0xff));
        }

        return byteList;
    }

    /// <summary>
    ///  写多个线圈
    /// </summary>
    /// <param name="slaveAddress">从设备的地址</param>
    /// <param name="startAddress">要写入的起始地址</param>
    /// <param name="data">要写入的值</param>
    static writeMultipleCoils(slaveAddress, startAddress, data) {
        return null;
    }


}

module.exports=ModBusTcpMasterUtils;